<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Channel\Server as CHServer;
use Channel\Client;
require 'vendor/autoload.php';


$arr = new IniParser('proxy.ini');
$opt = $arr->parse();

$server = new CHServer(
    $opt->common->master,
    $opt->common->port
);

/**
 * 响应事件，接收信息后打开或关闭代理服务器
 */
class Server extends Worker {

    protected $_worker = [];

    protected $opt = [];

    public function __construct($opt, $socket_name = '', array $context_option = array())
    {
        parent::__construct($socket_name, $context_option);
        $this->opt = $opt;
        $this->onWorkerStart = function (){
            $this->onClientStart();
        };
    }


    protected static function trace($msg){
        printf("[%s]\t%s\n", date("Y-m-d H:i:s"), $msg);
    }
    /**
     * Client 启动。Client用于监听/注册来自Client需要代理的服务，
     * 注册服务: 监听来自Client的注册请求，并启动端口服务
     * 推送服务: 接收来自TCP链接的信息，并把信息推送给Client
     * @throws Exception
     */
    protected function onClientStart(){

        // 连接成功后，注册下列事件
        Client::$onConnect = function () {
            // 监听事件: 接收来自内网的端口转发要求。并启动端口的服务
            Client::on('register', function ($event_data) {
                self::trace("注册外部端口{$event_data['port']}");
                $port = $event_data['port'];            // 端口
                $identity = $event_data['identity'];    // Client注册带的唯一"身份ID", 并以此ID注册事件
                $this->start($port, $identity);
            });

            // 响应事件
            Client::on("send", function ($event_data){
                $id = $event_data['conn_id'];
                $identity = $event_data['identity'];
                $method = $event_data['method'];
                $send_bytes = $event_data['message'];

                if (!isset($this->connections[$id])) {
                    Client::publish($identity, [
                        'method' => 'close',    // 关闭
                        'message' => 'connection closed'
                    ]);
                    return;
                }
                $conn = $this->connections[$id];
                $conn->$method($send_bytes);
            });


            // 接收来自
            Client::on('close', function ($event_data) {
                $id = $event_data['conn_id'];
                $identity = $event_data['identity'];
                Client::publish($identity, [
                    'method' => 'close',    // 关闭
                    'message' => 'connection closed'
                ]);
                self::trace( "$id closed");
                unset($this->connections[$identity]);
            });
        };

        Client::$onClose = function (){
            Client::clearTimer();
            sleep(5);
            $this->onClientStart();
        };

        Client::connect(
            $this->opt->common->master,
            $this->opt->common->port
        );
    }

    /**
     * 启动代理端口
     * @param $port
     * @param $identity
     * @throws Exception
     */
    protected function start($port, $identity){
        // 如果已经启动这个代理后，那么将不再启动
        if (isset($this->_worker[$identity])) {
            return ;
        }

        $server = new Worker("tcp://0.0.0.0:".$port);
        $server->listen();

        // 当有访问代理端口的请求时
        $server->onConnect = function (TcpConnection $guest) use ($identity){
            // 把请求的连接告发送代理服务事件。
            Client::publish($identity, [
                'method' => 'connect',
                'conn_id' => $guest->getRemoteAddress(),
            ]);

            // 请求链接的数据转发到代理服务事件
            $guest->onMessage = function (TcpConnection $connection, $recv_bytes) use($identity){
                Client::publish($identity, [
                    'method' => 'send',
                    'conn_id' => $connection->id,
                    'message' => $recv_bytes
                ]);
            };

            // 同样的，当客户端连接失败或关闭时，也将数据发送给代理服务事件
            $cb = function (TcpConnection $connection) use($identity){
                Client::publish($identity, [
                    'method' => 'close',
                    'guest' => $connection->id,
                    'message' => ''
                ]);
                unset($this->connections[$connection->getRemoteAddress()]);
            };

            $guest->onClose = $cb;
            $guest->onError = $cb;
            $this->connections[$guest->getRemoteAddress()] = $guest;

        };
        $this->_worker[$identity] = $server;
    }

}

new Server($opt);
// 采用Swoole事件轮序
Worker::$eventLoopClass = 'Workerman\Events\Swoole';

Worker::runAll();